package com.wlyuan.index.domain.source;

import com.alibaba.fastjson.JSON;
import com.wlyuan.core.domain.Identifiable;
import com.wlyuan.core.domain.valueobj.*;
import com.wlyuan.index.api.dto.QuotationIndexDTO;
import com.wlyuan.index.api.dto.Versioned;
import com.wlyuan.index.api.dto.combine.CombineIndexDTO;
import com.wlyuan.index.api.dto.order.OrderIndexDTO;
import com.wlyuan.index.api.dto.waybill.BalanceIndexDTO;
import com.wlyuan.index.api.dto.waybill.BillIndexDTO;
import com.wlyuan.index.api.dto.waybill.WaybillIndexDTO;
import com.wlyuan.index.domain.source.valueobj.Modifiers;
import com.wlyuan.index.domain.source.valueobj.SourceId;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.collections4.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@AllArgsConstructor
@ToString
public class Source implements Identifiable<SourceId>, Serializable {
    private SourceId id;
    private String dataJson;
    private String dataClass;
    private Long version;
    private final List<SourceId> boundIds = new ArrayList<>();

    public <T extends Versioned> Source(SourceId sourceId, T data) {
        this.id = sourceId;
        this.dataJson = JSON.toJSONString(data);
        this.dataClass = data.getClass().getSimpleName();
        this.version = data.getVersion();
    }

    public Source(OrderIndexDTO order) {
        this(new SourceId(new OrderId(order.getOrderId())), order);
    }

    public Source(WaybillIndexDTO waybill) {
        this(new SourceId(new WaybillId(waybill.getWaybillId())), waybill);
    }

    public Source(CombineIndexDTO combine) {
        this(new SourceId(new CombineId(combine.getCombineId())), combine);
    }

    public Source(QuotationIndexDTO quotation) {
        this(new SourceId(new QuotationId(quotation.getQuotationId())), quotation);
    }

    public Source(BillIndexDTO bill) {
        this(new SourceId(new BillId(bill.getBillId())), bill);
    }

    public Source(BalanceIndexDTO balance) {
        this(new SourceId(Modifiers.PAYMENT, new WaybillId(balance.getWaybillId())), balance);
    }

    public <T> T decode(Class<T> dataClass) {
        return JSON.parseObject(dataJson, dataClass);
    }

    public <T> T decode() {
        if (this.isQuotation()) {
            return (T) this.decode(QuotationIndexDTO.class);
        } else if (this.isBill()) {
            return (T) this.decode(BillIndexDTO.class);
        } else if (this.isBalance()) {
            return (T) this.decode(BalanceIndexDTO.class);
        } else if (this.isCombine()) {
            return (T) this.decode(CombineIndexDTO.class);
        } else if (this.isWaybill()) {
            return (T) this.decode(WaybillIndexDTO.class);
        } else if (this.isOrder()) {
            return (T) this.decode(OrderIndexDTO.class);
        }
        return null;
    }

    public void addBounds(Collection<SourceId> boundIds) {
        this.boundIds.addAll(boundIds);
    }

    public boolean hasBounds() {
        return CollectionUtils.isNotEmpty(this.boundIds);
    }

    public boolean isOrder() {
        return OrderIndexDTO.class.getSimpleName().equals(this.dataClass);
    }

    public boolean isWaybill() {
        return WaybillIndexDTO.class.getSimpleName().equals(this.dataClass);
    }

    public boolean isCombine() {
        return CombineIndexDTO.class.getSimpleName().equals(this.dataClass);
    }

    public boolean isQuotation() {
        return QuotationIndexDTO.class.getSimpleName().equals(this.dataClass);
    }

    public boolean isBill() {
        return BillIndexDTO.class.getSimpleName().equals(this.dataClass);
    }

    public boolean isBalance() {
        return BalanceIndexDTO.class.getSimpleName().equals(this.dataClass);
    }


    public List<SourceId> getBoundIds(Modifiers modifier) {
        if (CollectionUtils.isEmpty(boundIds)) {
            return Collections.emptyList();
        }

        return this.boundIds.stream()
                .filter(boundId -> modifier == boundId.getModifier())
                .collect(Collectors.toList());
    }

    public <T> T getBusinessId() {
        return this.id.decode();
    }
}
